import * as BL from "../index";


/**
 * 引擎容器可选参数对象接口
 */
interface IContainerOptions {
    /**
     * 开启抗锯齿
     */
    antialias?: boolean,
    /**
     * 自适应设备分辨率
     */
    adaptToDeviceRatio?: boolean,
    /**
     * 引擎的初始化参数对象
     */
    engineOptions?: BL.EngineOptions,
    /**
     * 显示性能监视器（帧率）
     */
    showPerformance?: boolean,
    /**
     * 使用右手系
     */
    useRightHand?: boolean,
}

/**
 * 引擎容器组件
 */
abstract class BabylonContainer {

    public static isRightHand: boolean = false;
    public static engine: BL.Engine | null = null;
    public static audioEngine: BL.AudioEngineV2 | null = null;

    public body!: HTMLDivElement;
    public ui!: HTMLDivElement;
    public performance?: HTMLDivElement;
    public canvas!: HTMLCanvasElement;
    public engine!: BL.Engine;
    public stageManager!: BL.StageManager;
    public buffer!: BL.BufferLoad;

    private _clientRect!: DOMRect;
    private _firstUserGesture: boolean = true;

    constructor() {
        this.initializeCanvas();

        const resizeObserver = new ResizeObserver((entries) => {
            for (let i = 0; i < entries.length; i++) {
                this.engine.resize();
            }
            this.onWindowResize();
        });
        resizeObserver.observe(this.canvas as unknown as Element);

        this.initialize3DContent();

    }

    /**
     * 获取只读的向前向量
     */
    public static get forward(): BL.Vector3 {
        return BabylonContainer.isRightHand ? BL.Vector3.RightHandedForwardReadOnly : BL.Vector3.LeftHandedForwardReadOnly;
    }

    /**
     * 获取只读的向后向量
     */
    public static get backward(): BL.Vector3 {
        return BabylonContainer.isRightHand ? BL.Vector3.LeftHandedForwardReadOnly : BL.Vector3.RightHandedForwardReadOnly;
    }

    /**
     * 创建设置过手系的场景
     * @returns 设置过手系的场景
     */
    public static createScene(): BL.Scene | null {
        if (!BabylonContainer.engine) {
            console.error('missing available engine!');
            return null;
        }
        const scn = new BL.Scene(BabylonContainer.engine);
        scn.useRightHandedSystem = BabylonContainer.isRightHand;
        return scn;
    }

    /**
     * 设置需要预加载的资源信息
     * @returns 资源路径对象
     */
    protected preloadAssetsUrl(): BL.ILoose<string> {
        return {};
    }

    /**
     * 资源加载UI（用于替换默认加载UI）
     * @returns UI（可为null）
     */
    protected loadingUI(): HTMLElement | null {
        return null;
    }

    /**
     * 设置容器参数
     * @returns 容器参数对象
     */
    protected options(): IContainerOptions {
        return {
            adaptToDeviceRatio: true,
            showPerformance: false,
            engineOptions: {
                limitDeviceRatio: 1.5,
            },
        };
    }

    /**
     * 渲染dom类型的UI界面
     */
    protected renderUI(): void {
    }

    /**
     * 初始化舞台
     * @param engine 引擎 
     * @param canvas 画布
     * @param bufferLoader 资源预加载器
     */
    protected abstract initializeStages(engine: BL.Engine, canvas: HTMLCanvasElement, bufferLoader: BL.BufferLoad): Array<BL.Stage>;

    /**
     * 渲染循环
     */
    private renderLoop(): void {
        this.stageManager.render();
    }

    /**
     * 初始化3D内容
     */
    private async initialize3DContent(): Promise<void> {
        if ((window as any).notSupportWebGL !== undefined) {
            alert('failed to setup 3D content, the browser does not support webgl!');
            return;
        }
        delete (window as any).notSupportWebGL;
        const opt = this.options();
        BabylonContainer.isRightHand = !!opt.useRightHand;
        this.engine = new BL.Engine(this.canvas, opt.antialias, opt.engineOptions, opt.adaptToDeviceRatio);
        this.engine.disableUniformBuffers = true;
        BabylonContainer.engine = this.engine;

        BabylonContainer.audioEngine = await BL.CreateAudioEngineAsync();
        BabylonContainer.audioEngine.volume = 1;

        this.engine.loadingScreen = new BL.DefaultLoadingScreen(this.canvas, '', this.loadingUI());
        this.dispatchPageLoadedEvent();

        this.engine.loadingScreen.displayLoadingUI();
        this.buffer = new BL.BufferLoad();
        await this.preloadAssets();

        this.stageManager = new BL.StageManager();
        const stages = this.initializeStages(this.engine, this.canvas, this.buffer);
        this.stageManager.add(...stages);
        this.setupUI(stages);
        this.stageManager.enable(0);
        this.engine.runRenderLoop(this.renderLoop.bind(this));

        window.addEventListener('pointerup', this.onUserGesture.bind(this));

        this.setPerformanceMonitor(opt);

    }

    private setPerformanceMonitor(option: IContainerOptions): void {
        if (option.showPerformance) {
            this.engine.disablePerformanceMonitorInBackground = false;
            const monitor = this.engine.performanceMonitor;
            const panel = this.performance!;
            let t = 0;
            const ani = () => {
                t += 0.05;
                if (t > 1) {
                    t = 0;
                    const fps = Math.round(monitor.averageFPS);
                    const aft = Math.round(monitor.averageFrameTime);
                    const rat = Math.pow(fps / 60, 2);
                    panel.style.color = 'rgb(' + ((1 - rat) * 255) + ',' + (rat * 255) + ',0)';
                    panel.innerText = 'average frames per second:' + fps + '\n' + 'average frame time:' + aft + 'ms';
                }
                requestAnimationFrame(ani);
            };
            ani();
        } else {
            this.engine.disablePerformanceMonitorInBackground = true;
        }
    }

    /**
     * 创建ui界面(重写实现其它创建规则)
     * @param stages 
     */
    protected setupUI(stages: BL.Stage[]): void {
        stages[0].onPreparedObservable.addOnce(() => {
            this.renderUI();
        });
    }

    /**
     * 用户行为
     */
    protected onUserGesture(): void {
        if (this._firstUserGesture) {
            BabylonContainer.audioEngine?.unlockAsync().then(() => {
                this._firstUserGesture = false;
                this.stageManager.onUserGesture();
            });
        } else {
            this.stageManager.onUserGesture();
        }
    }

    /**
     * 窗体变化
     */
    protected onWindowResize(): void {
        const currentRect = this.canvas.getBoundingClientRect();
        const sizeChange = (currentRect.width !== this._clientRect.width) || (currentRect.height !== this._clientRect.height);
        if (!sizeChange) {
            return;
        }
        this._clientRect = currentRect;
        this.engine.resize();
        if (this.stageManager) this.stageManager.onWindowResize(currentRect);
    }

    /**
     * 初始化容器元素,画布元素及UI容器元素
     */
    private initializeCanvas() {
        const body = document.createElement('div');
        body.id = 'root-container-body';
        body.style.left = '0';
        body.style.right = '0';
        body.style.top = '0';
        body.style.bottom = '0';
        body.style.touchAction = 'none';
        body.style.position = 'absolute';
        body.style.display = 'block';
        body.style.backgroundColor = 'rgba(0,0,0,0)';

        const canvas = document.createElement('canvas');
        canvas.id = 'root-render-canvas';
        canvas.style.width = '100%';
        canvas.style.height = '100%';
        canvas.style.touchAction = 'none';
        canvas.style.position = 'absolute';
        canvas.style.display = 'block';
        canvas.style.outline = '0';
        body.appendChild(canvas);

        const ui = document.createElement('div');
        ui.id = 'root-ui-container';
        ui.style.left = '0';
        ui.style.right = '0';
        ui.style.top = '0';
        ui.style.bottom = '0';
        ui.style.touchAction = 'none';
        ui.style.pointerEvents = 'none';
        ui.style.position = 'absolute';
        ui.style.display = 'block';
        ui.style.zIndex = '100';
        body.appendChild(ui);

        if (this.options().showPerformance) {
            const pf = document.createElement('div');
            pf.id = 'performance-monitor';
            pf.style.right = '0';
            pf.style.top = '0';
            pf.style.width = '250px';
            pf.style.height = '50px';
            pf.style.touchAction = 'none';
            pf.style.pointerEvents = 'none';
            pf.style.position = 'fixed';
            pf.style.zIndex = '1000';
            pf.style.background = 'rgba(0,0,0,0)';
            body.appendChild(pf);
            this.performance = pf;
        }


        this.body = body;
        this.canvas = canvas;
        this.ui = ui;
        this._clientRect = canvas.getBoundingClientRect();
    }

    /**
     * 预加载资源
     * @returns 资源预加载信息集合
     */
    private async preloadAssets(): Promise<BL.IBufferLoadInfo[]> {
        return this.buffer.load(this.preloadAssetsUrl());
    }

    /**
     * 发送页面加载完成事件（移除页面加载UI）
     */
    private dispatchPageLoadedEvent(): void {
        const event = new Event('page-loaded');
        window.dispatchEvent(event);
    }

}


export { BabylonContainer };
export type { IContainerOptions };